package cn.s1995.sloth.pay.biz.handler.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.map.MapUtil;
import cn.hutool.json.JSONArray;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import cn.s1995.sloth.bizs.constant.BizCst;
import cn.s1995.sloth.bizs.entity.Order;
import cn.s1995.sloth.bizs.entity.OrderRefund;
import cn.s1995.sloth.bizs.entity.PayFlow;
import cn.s1995.sloth.bizs.event.OrderPaySuccessEvent;
import cn.s1995.sloth.bizs.service.OrderRefundService;
import cn.s1995.sloth.bizs.service.OrderService;
import cn.s1995.sloth.bizs.service.PayFlowService;
import cn.s1995.sloth.common.core.util.StringUtil;
import cn.s1995.sloth.common.core.constant.CommonConstants;
import cn.s1995.sloth.common.data.tenant.TenantContextHolder;
import cn.s1995.sloth.pay.biz.plugin.paypal.PaypalApiPlus;
import cn.s1995.sloth.pay.biz.service.BizPayService;
import cn.s1995.sloth.pay.entity.PayChannel;
import cn.s1995.sloth.pay.entity.PayNotifyRecord;
import cn.s1995.sloth.pay.handler.MessageDuplicateCheckerHandler;
import cn.s1995.sloth.pay.mapper.PayChannelMapper;
import cn.s1995.sloth.pay.service.PayNotifyRecordService;
import cn.s1995.sloth.pay.utils.PayChannelNameEnum;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.ijpay.paypal.PayPalApiConfigKit;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.stereotype.Service;

import java.time.LocalDateTime;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;

/**
 * BizPaypalNotifyCallbackHandler
 *
 * @author Lex
 * @description
 * @date 2024/01/06 02:06
 */
@Slf4j
@AllArgsConstructor
@Service
public class BizPaypalNotifyCallbackHandler extends AbstractBizPayNotifyCallbackHandler {

    private final MessageDuplicateCheckerHandler duplicateCheckerHandler;
    private final ApplicationEventPublisher applicationEventPublisher;
    private final HttpServletRequest request;
    private final HttpServletResponse response;
    private final PayNotifyRecordService recordService;
    private final PayFlowService payFlowService;
    private final OrderService orderService;
    private final OrderRefundService orderRefundService;
    private final PayChannelMapper channelMapper;
    private final BizPayService bizPayService;


    @Override
    public void before(Map<String, Object> params) {

        try {
            if (Objects.isNull(PayPalApiConfigKit.getApiConfig())) {
                PayChannel channel = channelMapper.selectOne(
                        Wrappers.lambdaQuery(PayChannel.class)
                                .eq(PayChannel::getChannelId, PayChannelNameEnum.PAYPAL_PAY.name())
                                .eq(PayChannel::getState, BizCst.CommonStatusType.enable),
                        Boolean.FALSE
                );
                PaypalApiPlus.initPaypalConfig(channel);
            }
        } catch (Exception e) {
            log.error("Paypal支付渠道配置为空，初始化Paypal配置");
            PayChannel channel = channelMapper.selectOne(
                    Wrappers.lambdaQuery(PayChannel.class)
                            .eq(PayChannel::getChannelId, PayChannelNameEnum.PAYPAL_PAY.name())
                            .eq(PayChannel::getState, BizCst.CommonStatusType.enable),
                    Boolean.FALSE
            );
            PaypalApiPlus.initPaypalConfig(channel);
        }


        if (params.containsKey(CommonConstants.TENANT_ID)) {
            Long tenant = MapUtil.getLong(params, CommonConstants.TENANT_ID);
            TenantContextHolder.setTenantId(tenant);
        }
    }


    @Override
    public Boolean duplicateChecker(Map<String, Object> params) {

        // 判断10秒内是否已经回调处理
        if (duplicateCheckerHandler.isDuplicate(params.get("id").toString())) {
            log.info("Paypal重复回调 {} 不做处理", params);
            this.saveNotifyRecord(params, "重复回调");
            return true;
        }

        return false;
    }

    @Override
    public Boolean verifyNotify(Map<String, Object> params) {
        boolean verifyWebhookSign = PaypalApiPlus.verifyWebhookSign(PayPalApiConfigKit.getApiConfig(), request, params);
        log.info("Paypal回调验签结果 - {}", verifyWebhookSign);
        return true;
    }

    @Override
    public String parse(Map<String, Object> params) {
        String eventType = params.get("event_type").toString();
        switch (eventType) {
            // A buyer approved a checkout order -> 买家批准了结帐订单
            case "CHECKOUT.ORDER.APPROVED" -> {
                log.info("{} -> 买家批准了结帐订单", eventType);
                // 此时买家账号钱包已扣款，可进行支付成功处理
                paySuccessHandle(params);
            }
            // A checkout order is processed. Note: For use by marketplaces and platforms only. -> 结帐订单已处理。注意：仅供市场和平台使用。
            case "CHECKOUT.ORDER.COMPLETED" -> {
                log.info("{} -> 结帐订单已处理", eventType);
                // 支付捕获完成处理
                payCaptureCompleteHandle(params);
            }
            case "PAYMENT.CAPTURE.PENDING" -> {
                log.info("{} -> 支付捕获挂起", eventType);
            }
            case "PAYMENT.CAPTURE.DECLINED", "PAYMENT.CAPTURE.DENIED" -> {
                log.info("{} -> 支付捕获被拒绝", eventType);
                payFailureHandle(params);
            }

            // A merchant refunds a payment capture. -> 商家退还付款捕获。
            case "PAYMENT.CAPTURE.REFUNDED" -> {
                log.info("{} -> 商家退还付款捕获", eventType);
                refundSuccessHandle(params);
            }



            // A problem occurred after the buyer approved the order but before you captured the payment. Refer to Handle uncaptured payments for what to do when this event occurs. -> 买方批准订单后，但在您捕获付款之前发生问题。有关此事件发生时要执行的操作，请参阅处理未捕获的付款。
            case "CHECKOUT.PAYMENT-APPROVAL.REVERSED" -> {
                log.info("{} -> 买方批准订单后，但在您捕获付款之前发生问题", eventType);
            }
            // A payment capture completes. -> 支付捕获完成。
            case "PAYMENT.CAPTURE.COMPLETED" -> {
                log.info("{} -> 支付捕获完成", eventType);
            }

            default -> {
                log.info("{} -> Paypal渠道支付回调未找到对应事件类型", eventType);
            }
        }

        return "success";
    }


    @Override
    public void saveNotifyRecord(Map<String, Object> params, String result) {
        PayNotifyRecord record = new PayNotifyRecord();
        record.setHttpStatus(String.valueOf(response.getStatus()));
        String notifyId = params.get("id").toString();
        saveRecord(params, result, record, notifyId, recordService);
    }

    /**
     * 处理-订单批准付款
     *
     * @param params
     */
    private void paySuccessHandle(Map<String, Object> params) {
        log.info("paySuccessHandle - {}", JSONUtil.toJsonStr(params));
        // {"event_version":"1.0","summary":"A $ 1.0 USD capture payment was refunded","event_type":"PAYMENT.CAPTURE.REFUNDED","create_time":"2024-01-22T13:24:44.838Z","resource":{"seller_payable_breakdown":{"total_refunded_amount":{"value":"5.00","currency_code":"USD"},"paypal_fee":{"value":"0.00","currency_code":"USD"},"gross_amount":{"value":"1.00","currency_code":"USD"},"net_amount":{"value":"1.00","currency_code":"USD"}},"amount":{"value":"1.00","currency_code":"USD"},"update_time":"2024-01-22T05:24:40-08:00","create_time":"2024-01-22T05:24:40-08:00","invoice_id":"88275d59478440b3bc21848caeda57f9","links":[{"method":"GET","rel":"self","href":"https://api.sandbox.paypal.com/v2/payments/refunds/3AM73523MV8592903"},{"method":"GET","rel":"up","href":"https://api.sandbox.paypal.com/v2/payments/captures/1JV25485S3420393G"}],"id":"3AM73523MV8592903","note_to_payer":"order refund","payer":{"email_address":"
        JSONObject resource = JSONUtil.parseObj(params.get("resource"));
        if (!resource.containsKey("id")) {
            log.info("Paypal支付成功处理 - 交易id为空");
            return;
        }
        String transactionId = resource.get("id").toString();
        PayFlow payFlow = payFlowService.getPayFlowByTransactionId(transactionId);
        if (Objects.isNull(payFlow)) {
            log.info("支付流水不存在 - {}", transactionId);
            return;
            // throw new BizException(Hint.APP_ORDER_NOT_EXIST);
        }
        LocalDateTime now = LocalDateTime.now();
        // 修改支付流水状态
        boolean payFlowUpdate = payFlowService.update(
                Wrappers.lambdaUpdate(PayFlow.class)
                        .set(PayFlow::getPayState, BizCst.PayFlow.State.paid)
                        .set(PayFlow::getPaySuccTime, now)
                        .set(PayFlow::getVersion, payFlow.getVersion() + 1)
                        .eq(PayFlow::getId, payFlow.getId())
                        .eq(PayFlow::getVersion, payFlow.getVersion())
        );
        if (!payFlowUpdate) {
            log.info("支付流水状态已改变 - {}", transactionId);
            return;
            // throw new BizException(Hint.APP_BILLING_INFO_HANGED);
        }

        List<Order> orderList = orderService.getOrderListByPayFlowId(payFlow.getId());
        if (CollUtil.isEmpty(orderList)) {
            log.info("订单不存在 - {}", transactionId);
            return;
            // throw new BizException(Hint.APP_ORDER_NOT_EXIST);
        }

        // 将订单改为已支付状态
        orderService.update(
                Wrappers.lambdaUpdate(Order.class)
                        .set(Order::getTransactionId, payFlow.getTransactionId())
                        .set(Order::getState, BizCst.Order.State.paid)
                        .set(Order::getIsPayed, Boolean.TRUE)
                        .set(Order::getPayMethod, BizCst.PayMethod.paypal)
                        .set(Order::getPayTime, now)
                        .in(Order::getId, orderList.stream().map(Order::getId).collect(Collectors.toList()))
        );

        // 完成订单(捕获订单)
        orderList.forEach(order -> {
            bizPayService.captureOrder(order.getPayFlowId().toString(), Boolean.FALSE);
        });

        // 发送支付成功事件 - 用于发送短信、邮件等
        applicationEventPublisher.publishEvent(new OrderPaySuccessEvent(orderList));
    }

    /**
     * 支付捕获完成处理
     *
     * @param params
     */
    private void payCaptureCompleteHandle(Map<String, Object> params) {
        log.info("payCaptureCompleteHandle - {}", JSONUtil.toJsonStr(params));
        // {"event_version":"1.0","summary":"A $ 1.0 USD capture payment was refunded","event_type":"PAYMENT.CAPTURE.REFUNDED","create_time":"2024-01-22T13:24:44.838Z","resource":{"seller_payable_breakdown":{"total_refunded_amount":{"value":"5.00","currency_code":"USD"},"paypal_fee":{"value":"0.00","currency_code":"USD"},"gross_amount":{"value":"1.00","currency_code":"USD"},"net_amount":{"value":"1.00","currency_code":"USD"}},"amount":{"value":"1.00","currency_code":"USD"},"update_time":"2024-01-22T05:24:40-08:00","create_time":"2024-01-22T05:24:40-08:00","invoice_id":"88275d59478440b3bc21848caeda57f9","links":[{"method":"GET","rel":"self","href":"https://api.sandbox.paypal.com/v2/payments/refunds/3AM73523MV8592903"},{"method":"GET","rel":"up","href":"https://api.sandbox.paypal.com/v2/payments/captures/1JV25485S3420393G"}],"id":"3AM73523MV8592903","note_to_payer":"order refund","payer":{"email_address":"
        JSONObject resource = JSONUtil.parseObj(params.get("resource"));
        if (!resource.containsKey("id")) {
            log.info("Paypal捕获完成处理 - 交易id为空");
            return;
        }
        if (resource.containsKey("purchase_units") && resource.get("purchase_units") instanceof JSONArray purchaseUnits && CollUtil.isNotEmpty(purchaseUnits)) {
            for (int i = 0; i < purchaseUnits.size(); i++) {
                JSONObject purchaseUnit = purchaseUnits.getJSONObject(i);
                // 订单id
                String orderId = purchaseUnit.get("reference_id").toString();
                if (purchaseUnit.containsKey("payments") && purchaseUnit.get("payments") instanceof JSONObject payments) {
                    if (payments.containsKey("captures") && payments.get("captures") instanceof JSONArray captures && CollUtil.isNotEmpty(captures)) {
                        JSONObject capture = captures.getJSONObject(0);
                        if (capture.containsKey("id")) {
                            // 更新订单捕获id
                            orderService.update(
                                    Wrappers.lambdaUpdate(Order.class)
                                            .set(Order::getCaptureId, capture.get("id").toString())
                                            .eq(Order::getId, orderId)
                            );
                        }
                    }
                }
            }
        }


    }


    /**
     * 退款成功处理
     *
     * @param params
     */
    private void refundSuccessHandle(Map<String, Object> params) {
        log.info("refundSuccessHandle - {}", JSONUtil.toJsonStr(params));
        // {"event_version":"1.0","summary":"A $ 1.0 USD capture payment was refunded","event_type":"PAYMENT.CAPTURE.REFUNDED","create_time":"2024-01-22T13:24:44.838Z","resource":{"seller_payable_breakdown":{"total_refunded_amount":{"value":"5.00","currency_code":"USD"},"paypal_fee":{"value":"0.00","currency_code":"USD"},"gross_amount":{"value":"1.00","currency_code":"USD"},"net_amount":{"value":"1.00","currency_code":"USD"}},"amount":{"value":"1.00","currency_code":"USD"},"update_time":"2024-01-22T05:24:40-08:00","create_time":"2024-01-22T05:24:40-08:00","invoice_id":"88275d59478440b3bc21848caeda57f9","links":[{"method":"GET","rel":"self","href":"https://api.sandbox.paypal.com/v2/payments/refunds/3AM73523MV8592903"},{"method":"GET","rel":"up","href":"https://api.sandbox.paypal.com/v2/payments/captures/1JV25485S3420393G"}],"id":"3AM73523MV8592903","note_to_payer":"order refund","payer":{"email_address":"sb-usv4d29079274@business.example.com","merchant_id":"RUSBAMRHEW9H2"},"status":"COMPLETED"},"resource_type":"refund","links":[{"href":"https://api.sandbox.paypal.com/v1/notifications/webhooks-events/WH-3KU809792X340024B-80U981367W909604U","rel":"self","method":"GET"},{"href":"https://api.sandbox.paypal.com/v1/notifications/webhooks-events/WH-3KU809792X340024B-80U981367W909604U/resend","rel":"resend","method":"POST"}],"id":"WH-3KU809792X340024B-80U981367W909604U","resource_version":"2.0"}
        JSONObject resource = JSONUtil.parseObj(params.get("resource"));
        if (!resource.containsKey("id")) {
            log.info("Paypal退款成功处理 - 交易id为空");
            return;
        }
        // paypal退款id
        String id = resource.get("id").toString();
        // 业务退款id
        String refundId = resource.get("custom_id").toString();
        // 业务订单id
        String orderId = resource.get("invoice_id").toString();
        // 退款状态
        String status = resource.get("status").toString();
        if (!"COMPLETED".equals(status)) {
            log.info("Paypal退款成功处理 - 退款状态不是COMPLETED");
            return;
        }

        //退款业务逻辑处理
    	orderRefundService.update(
    			Wrappers.lambdaUpdate(OrderRefund.class)
    					.set(OrderRefund::getRefundTime, LocalDateTime.now())
    					.set(OrderRefund::getState, BizCst.OrderRefund.State.completed)
    					.set(OrderRefund::getOutRefundNo, id)
    					.eq(OrderRefund::getId, refundId)
    	);
    }

    /**
     * 支付失败处理
     * @param params
     */
    private void payFailureHandle(Map<String, Object> params) {
        log.info("payFailureHandle - {}", JSONUtil.toJsonStr(params));
        // {"id":"WH-8Y6524781B2662740-6BA13970S75831204","create_time":"2015-10-07T16:58:15Z","resource_type":"capture","event_type":"PAYMENT.CAPTURE.DENIED","summary":"A EUR 0.5 EUR capture payment was denied","resource":{"parent_payment":"PAY-6AG64245W53716013KYKUWNI","update_time":"2015-10-07T16:42:57Z","amount":{"total":"0.50","currency":"EUR"},"create_time":"2015-10-07T16:42:50Z","links":[{"href":"https://api.paypal.com/v1/payments/capture/1YH34677BD7819356","rel":"self","method":"GET"},{"href":"https://api.paypal.com/v1/payments/capture/1YH34677BD7819356/refund","rel":"refund","method":"POST"},{"href":"https://api.paypal.com/v1/payments/payment/PAY-6AG64245W53716013KYKUWNI","rel":"parent_payment","method":"GET"}],"id":"1YH34677BD7819356","state":"denied"},"links":[{"href":"https://api.paypal.com/v1/notifications/webhooks-events/WH-8Y6524781B2662740-6BA13970S75831204","rel":"self","method":"GET","encType":"application/json"},{"href":"https://api.paypal.com/v1/notifications/webhooks-events/WH-8Y6524781B2662740-6BA13970S75831204/resend","rel":"resend","method":"POST","encType":"application/json"}],"event_version":"1.0"}
        JSONObject resource = JSONUtil.parseObj(params.get("resource"));
        if (!resource.containsKey("id")) {
            log.info("Paypal支付成功处理 - 交易id为空");
            return;
        }
        String transactionId = resource.get("id").toString();
        PayFlow payFlow = payFlowService.getPayFlowByTransactionId(transactionId);
        if (Objects.isNull(payFlow)) {
            log.info("支付流水不存在 - {}", transactionId);
            return;
            // throw new BizException(Hint.APP_ORDER_NOT_EXIST);
        }
        LocalDateTime now = LocalDateTime.now();
        // 修改支付流水状态
        boolean payFlowUpdate = payFlowService.update(
                Wrappers.lambdaUpdate(PayFlow.class)
                        .set(PayFlow::getPayState, BizCst.PayFlow.State.failure)
                        .set(PayFlow::getUpdateTime, now)
                        .set(PayFlow::getExtra, StringUtil.getValueOrEmpty(params.get("summary")))
                        .set(PayFlow::getVersion, payFlow.getVersion() + 1)
                        .eq(PayFlow::getId, payFlow.getId())
                        .eq(PayFlow::getVersion, payFlow.getVersion())
        );
        if (!payFlowUpdate) {
            log.info("支付流水状态已改变 - {}", transactionId);
            return;
            // throw new BizException(Hint.APP_BILLING_INFO_HANGED);
        }

    }

}
